package com.oig.sys.log.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.oig.common.module.SysRequest;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author qiuzebin
 * @date 2023/8/31
 */
public class LogQueryUtil {

    private final static String REGEX = ";";
    private final static String START_STR = "start";
    private final static String END_STR = "end";

    /**
     * 复杂查询 参数转换成 ES query
     * 只针对打了 @Schema 注解的属性，做自动转换
     *
     * @param request  参数实体
     * @param rangList 范围字段处理
     * @return 返回ES查询体
     */
    public static NativeSearchQueryBuilder buildQueryByDto(SysRequest request, List<String> rangList) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        Object paramDto = request.getParamDto();
        if (paramDto == null) {
            return builder;
        }
        JSONObject paramMap = parseDtoToMap(rangList, paramDto);
        if (paramMap.isEmpty()) {
            return builder;
        }
        BoolQueryBuilder boolQuery = buildQueryBuiler(rangList, paramMap);
        builder.withQuery(boolQuery);
        return builder;
    }

    /**
     * 生成ES查询实体Builder
     *
     * @param rangList 范围相关的参数
     * @param paramMap 数据参数
     * @return builder实体
     */
    private static BoolQueryBuilder buildQueryBuiler(List<String> rangList, Map<String, Object> paramMap) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
            Object valueObj = entry.getValue();
            String key = entry.getKey();
            if (rangList.contains(key)) {
                // 这里设置范围，比如 起始时间为： 2023-6 至2023-7
                String[] vauleArr = valueObj.toString().split(REGEX);
                String startValue = vauleArr[0];
                String endValue = null;
                if (vauleArr.length > 1) {
                    endValue = vauleArr[1];
                }
                if (StringUtils.isNotBlank(startValue)) {
                    boolQuery = boolQuery.must(QueryBuilders.rangeQuery(key).gte(startValue));
                }
                if (StringUtils.isNotBlank(endValue)) {
                    boolQuery = boolQuery.must(QueryBuilders.rangeQuery(key).lte(endValue));
                }
            } else {
                BoolQueryBuilder boolQueryTemp = QueryBuilders.boolQuery();
                boolQueryTemp = boolQueryTemp.should(QueryBuilders.matchPhraseQuery(key, valueObj));
                boolQueryTemp = boolQueryTemp.minimumShouldMatch(1);
                boolQuery = boolQuery.filter(boolQueryTemp);
            }
        }
        return boolQuery;
    }

    /**
     * 解析dto 处理时间范围的参数查询
     *
     * @param rangList 时间相关的字段
     * @param paramDto dto
     * @return 返回属性加结果
     */
    private static JSONObject parseDtoToMap(List<String> rangList, Object paramDto) {
        JSONObject dtoJsonObj = JSON.parseObject(JSON.toJSONString(paramDto));
        List<String> keySet = new ArrayList<>(dtoJsonObj.keySet());
        List<String> removeList = new ArrayList<>();
        for (String name : keySet) {
            Object value = dtoJsonObj.get(name);
            // 处理以 start、end的时间范围参数  5： start的长度， 3：end的长度
            int flag = name.startsWith(START_STR) ? 5 : (name.startsWith(END_STR) ? 3 : 0);
            if (flag == 0) {
                continue;
            }
            removeList.add(name);
            String nameTemp = name.substring(flag);
            // 由于驼峰，eg：startCreateTime 去掉start后就是CreateTime，需要首字母小写
            nameTemp = Character.toLowerCase(nameTemp.charAt(0)) + nameTemp.substring(1);
            if (rangList.contains(nameTemp)) {
                if (dtoJsonObj.containsKey(nameTemp)) {
                    Object oldObj = dtoJsonObj.get(nameTemp);
                    dtoJsonObj.put(nameTemp, flag == 5 ? (value + oldObj.toString()) : (oldObj.toString() + value));
                } else {
                    // 以 ； 分号拼接， 分号前是 xx时间之后， 分号后是 xx时间之前
                    dtoJsonObj.put(nameTemp, flag == 5 ? (value + REGEX) : (REGEX + value));
                }
            }
        }
        // 移除不必要的字段查询
        removeList.forEach(dtoJsonObj::remove);
        return dtoJsonObj;
    }
}
